/*******************************************************************************
 * Copyleft (c) 2021 Kcode
 *
 * @file    myuvc.c
 * @brief   实现USB摄像头的数据传输（框架）
 * @author  K
 * @version 0.0.1
 * @date    2021-07-21
 * @license MulanPSL-1.0
 *
 * 文件修改历史：
 * <时间>         | <版本>      | <作者>    | <描述>
 * 2021-07-21   | v0.0.1    | Kcode   | 实现USB摄像头的数据传输（框架）
 * -----------------------------------------------------------------------------
 ******************************************************************************/

#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/videodev2.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <asm/atomic.h>
#include <asm/unaligned.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-common.h>

static struct video_device *s_myuvc_vdev;	/**< video */

/*!
 * 所支持usb设备类的接口
 */
static struct usb_device_id myuvc_ids[] = {
	/* Generic USB Video Class */
	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) },	 /**< VideoControl interface */
	{ USB_INTERFACE_INFO(USB_CLASS_VIDEO, 2, 0) }, /**< VideoStreaming interface */
	{}
};

/*!
 * Step1  - 打开myuvc_fops设备文件
 */
static int myuvc_open(struct file *file)
{
	return 0;
}

/*!
 * 关闭myuvc_fops设备文件
 */
static int myuvc_close(struct file *file)
{
	return 0;
}

/*!
 * Step2 - 查询是否为USB摄像头设备
 */
static int myuvc_vidioc_querycap(struct file *file, void  *priv,
					struct v4l2_capability *cap)
{
	return 0;
}

/*!
 * Step3 - 列举USB摄像头设备所支持的格式format
 */
static int myuvc_vidioc_enum_fmt_vid_cap(struct file *file, 
							void *priv, struct v4l2_fmtdesc *f)
{
	return 0;
}

/*!
 * Step4 - 返回当前所使用的格式
 */
static int myuvc_vidioc_g_fmt_vid_cap(struct file *file, 
							void *priv, struct v4l2_format *f)
{
	return (0);
}

/*!
 * Step5 - 测试驱动程序是否支持某种格式
 */
static int myuvc_vidioc_try_fmt_vid_cap(struct file *file,
							void *priv, struct v4l2_format *f)
{
	return 0;
}

/*!
 * Step6 - 设置所支持的格式
 */
static int myuvc_vidioc_s_fmt_vid_cap(struct file *file,
							void *priv, struct v4l2_format *f)
{	
	return 0;
}

/*!
 * Step7 - 为该设备申请若干个缓冲区，分配头部信息
 */
static int myuvc_vidioc_reqbufs(struct file *file,
						void *priv, struct v4l2_requestbuffers *p)
{
	return 0;
}

/*!
 * Step8 - 查询缓冲区的信息，如大小、偏移地址等
 * 		得到信息后，APP可mmap进行地址映射，分配真正的存储数据的缓冲区
 */
static int myuvc_vidioc_querybuf(struct file *file, 
								void *priv, struct v4l2_buffer *p)
{
	return 0;
}

/*!
 * Step9 - APPmmap进行地址映射可直接操作这块内存，分配真正的存储数据的缓冲区
 */
static int myuvc_mmap(struct file *file, struct vm_area_struct *vma)
{
	return 0;
}

/*!
 * Step10 - 把申请的缓冲区放入队列，底层的硬件操作函数将会把数据放入队列
 */
static int myuvc_vidioc_qbuf(struct file *file, 
								void *priv, struct v4l2_buffer *p)
{
	return 0;
}

/*!
 * Step11 - 启动数据传输
 */
static int myuvc_vidioc_streamon(struct file *file, 
									void *priv, enum v4l2_buf_type i)
{
	return 0;
}

/*!
 * Step12 - APP调用poll/select确定缓存是否有数据
 */
static unsigned int myuvc_poll(struct file *file, 
									struct poll_table_struct *wait)
{
	return 0;
}


/*!
 * Step13 - APP通过poll/select确定缓冲区有数据后，从队列中取出并删除缓冲区
 */
static int myuvc_vidioc_dqbuf(struct file *file,
								void *priv, struct v4l2_buffer *p)
{
	return 0;
}

/*!
 * Step14 - APP已经mmap映射缓存，可直接读数据
 * Step15 - 再次调用myuvc_vidioc_dqbuf()，把缓存尾插法放入队列
 * Step16 - 在其调用myuvc_poll()
 */

/*!
 * Step17 - 不使用时，停止摄像头数据传输
 */
static int myuvc_vidioc_streamoff(struct file *file, 
									void *priv, enum v4l2_buf_type i)
{
	return 0;
}

/*!
 * 所支持的ioclt函数
 */
static const struct v4l2_ioctl_ops myuvc_ioctl_ops = {
	// 表示它是一个摄像头设备
	.vidioc_querycap      = myuvc_vidioc_querycap,

	/* 用于列举、获得、测试、设置摄像头的数据的格式 */
	.vidioc_enum_fmt_vid_cap  = myuvc_vidioc_enum_fmt_vid_cap,
	.vidioc_g_fmt_vid_cap     = myuvc_vidioc_g_fmt_vid_cap,
	.vidioc_try_fmt_vid_cap   = myuvc_vidioc_try_fmt_vid_cap,
	.vidioc_s_fmt_vid_cap     = myuvc_vidioc_s_fmt_vid_cap,

	/* 缓冲区操作: 申请/查询/放入队列/取出队列 */
	.vidioc_reqbufs       = myuvc_vidioc_reqbufs,
	.vidioc_querybuf      = myuvc_vidioc_querybuf,
	.vidioc_qbuf          = myuvc_vidioc_qbuf,
	.vidioc_dqbuf         = myuvc_vidioc_dqbuf,

	/* 启动/停止 */
	.vidioc_streamon      = myuvc_vidioc_streamon,
	.vidioc_streamoff     = myuvc_vidioc_streamoff,   
};

static const struct v4l2_file_operations myuvc_fops = {
	.owner			= THIS_MODULE,
    .open       	= myuvc_open,
    .release    	= myuvc_close,
    .mmap       	= myuvc_mmap,
    .unlocked_ioctl = video_ioctl2,
    .poll       	= myuvc_poll,
};

static void myuvc_release(struct video_device *vdev)
{}

static int myuvc_probe(struct usb_interface *intf,
		     const struct usb_device_id *id)
{	
	static int s_cnt = 0;
	
	printk("myuvc_probe : cnt = %d\n", s_cnt++);

	/*!
	 * myuvc_probe()调用第二次后执行
	 */
    if (s_cnt == 2) {
		/* 1、分配一个video_device结构体 */
		s_myuvc_vdev = video_device_alloc();

		/* 2、设置 */
		/* 注册过程需要用到release，必须设置 */
		s_myuvc_vdev->release     = myuvc_release;

		s_myuvc_vdev->fops        = &myuvc_fops;

		s_myuvc_vdev->ioctl_ops	  = &myuvc_ioctl_ops;

		/* 3、注册结构体 
		 * -1 - 自动分配次设备号
		 */
		video_register_device(s_myuvc_vdev, VFL_TYPE_GRABBER, -1);
	}
	
    return 0;
}
			 
static void myuvc_disconnect(struct usb_interface *intf)
{
	static int s_cnt = 0;

	printk("myuvc_disconnect : cnt = %d\n", s_cnt++);
	
	/*!
	 * myuvc_disconnect()调用第二次后执行
	 */
	if (s_cnt == 2) {
		/* 注销结构体 */
		video_unregister_device(s_myuvc_vdev);
		
		/* 释放结构体 */
		video_device_release(s_myuvc_vdev);		
	}
}

struct usb_driver myuvc_driver = {
	.name		= "myuvcvideo",
	.probe		= myuvc_probe,
	.disconnect	= myuvc_disconnect,
	.id_table	= myuvc_ids,
};

static int myuvc_init(void)
{
	int result;
	
	result = usb_register(&myuvc_driver);
	if (result == 0)
		printk("USB register error!\n");
	return result;
}

static void myuvc_cleanup(void)
{	
	usb_deregister(&myuvc_driver);
}

module_init(myuvc_init);
module_exit(myuvc_cleanup);

MODULE_LICENSE("GPL");


